﻿#include "SerialPort.h"

/********************************************************************************************************
        * PROGRAM      : QSerialPortTerminal
        * DATE - TIME  : vendredi 03 octobre 2008 - 11h15
        * AUTHOR       : VIANNEY-LIAUD Philippe ( philippe.vianney.liaud gmail.com )
            * FILENAME     : ManageSerialPort.cpp
            * LICENSE      : GPL
            * COMMENTARY   :
********************************************************************************************************/
#include <QDateTime>
#include <QtDebug>
#include <QEventLoop>

/********************************************************************************************************
        * Classe ManageSerialPort
        *****************************************************************************************************/

#if defined(WIN32)

    #include <WinSock2.h>
    #include <Windows.h>
#else
    #include <sys/time.h>
    #include <sys/times.h>
    #include <unistd.h>
#endif

//Constructeur
SerialPort::SerialPort()
{
    //Init pointeur a NULL
    threadSend = NULL;
    threadReceive = NULL;

    //Init des bool
    sendingEnabled = false;
    receivingEnabled = false;
    closeCalled = false;
    saveStateSendEnabled = false;
    saveStateReceivedEnabled = false;
    saveStateReceiveData = false;
}

SerialPort::SerialPort(const QString &name, const BaudRateType baudRate, \
        const DataBitsType dataBits, const ParityType parity, \
        const StopBitsType stopBits, const FlowType flowControl, \
        ulong milliseconds)
{
    //Init pointeur a NULL
    threadSend = NULL;
    threadReceive = NULL;

    //Init des bool
    sendingEnabled = false;
    receivingEnabled = false;
    closeCalled = false;
    saveStateSendEnabled = false;
    saveStateReceivedEnabled = false;
    saveStateReceiveData = false;

    setPort(name);
    setBaudRate(baudRate);
    setDataBits(dataBits);
    setParity(parity);
    setStopBits(stopBits);
    setFlowControl(flowControl);
    setTimeout(milliseconds);
}

//Destructeur
SerialPort::~SerialPort()
{
    if (threadSend != NULL)
    {
        delete threadSend;
        threadSend = NULL;
    }

    if (threadReceive != NULL)
    {
        delete threadReceive;
        threadReceive = NULL;
    }

    if (isOpen())
        extSerialPort.close();
}

bool SerialPort::open()
{
    bool res = extSerialPort.open(QIODevice::ReadWrite);

    if (closeCalled)
    {
        if (saveStateSendEnabled)
            enableSending();

        if (saveStateReceivedEnabled)
            enableReceiving();

        if (saveStateReceiveData)
            receiveData();
        closeCalled = false;
    }

    return res;
}

bool SerialPort::open(const QString &name, const BaudRateType baudRate, \
        const DataBitsType dataBits,const ParityType parity, \
        const StopBitsType stopBits, const FlowType flowControl, \
        ulong milliseconds)
{
    setPort(name);
    setBaudRate(baudRate);
    setDataBits(dataBits);
    setParity(parity);
    setStopBits(stopBits);
    setFlowControl(flowControl);
    setTimeout(milliseconds);

    bool res = extSerialPort.open(QIODevice::ReadWrite);


    return res;
}

bool SerialPort::isOpen()
{
    return extSerialPort.isOpen();
}

void SerialPort::close()
{
    closeCalled = true;
    saveStateSendEnabled = isSendingEnabled();
    saveStateReceivedEnabled = isReceivingEnabled();

//    disableSending();
//    disableReceiving();

    extSerialPort.close();
}

void SerialPort::setPort(const QString &name)
{
    extSerialPort.setPortName(name);
}

QString SerialPort::getPort()
{
    return extSerialPort.portName();
}


void SerialPort::setBaudRate(const BaudRateType baudRate)
{
    extSerialPort.setBaudRate(baudRate);
}

//QString SerialPort::getBaudRate()
//{
//	switch (extSerialPort.baudRate())
//	{
//		case BAUD50:
//			return QString("50");
//		case BAUD75:
//			return QString("75");
//		case BAUD110:
//			return QString("110");
//		case BAUD134:
//			return QString("134");
//		case BAUD150:
//			return QString("150");
//		case BAUD200:
//			return QString("200");
//		case BAUD300:
//			return QString("300");
//		case BAUD600:
//			return QString("600");
//		case BAUD1200:
//			return QString("1200");
//		case BAUD1800:
//			return QString("1800");
//		case BAUD2400:
//			return QString("2400");
//		case BAUD4800:
//			return QString("4800");
//		case BAUD9600:
//			return QString("9600");
//		case BAUD14400:
//			return QString("14400");
//		case BAUD19200:
//			return QString("19200");
//		case BAUD38400:
//			return QString("38400");
//		case BAUD56000:
//			return QString("56000");
//		case BAUD57600:
//			return QString("57600");
//		case BAUD76800:
//			return QString("76800");
//		case BAUD115200:
//			return QString("115200");
//		case BAUD128000:
//			return QString("128000");
//		case BAUD256000:
//			return QString("256000");
//	}
//    return "0";
//}

void SerialPort::setDataBits(const DataBitsType dataBits)
{
    extSerialPort.setDataBits(dataBits);
}

QChar SerialPort::getDataBits()
{
    switch (extSerialPort.dataBits())
    {
        case DATA_5:
            return QChar('5');
        case DATA_6:
            return QChar('6');
        case DATA_7:
            return QChar('7');
        case DATA_8:
            return QChar('8');
        }
        return 0;
}


void SerialPort::setParity(const ParityType parity)
{
    extSerialPort.setParity(parity);
}

QString SerialPort::getParity()
{
    switch (extSerialPort.parity())
    {
        case PAR_NONE:
            return QString(tr("None"));
        case PAR_ODD:
            return QString(tr("Odd"));
        case PAR_EVEN:
            return QString(tr("Even"));
#if defined(Q_OS_WIN) || defined(qdoc)
        case PAR_MARK:
            return QString(tr("Mark"));
#endif
        case PAR_SPACE:
            return QString(tr("Space"));
        }
        return 0;
}


void SerialPort::setStopBits(const StopBitsType stopBits)
{
    extSerialPort.setStopBits(stopBits);
}

QString SerialPort::getStopBit()
{
    switch (extSerialPort.stopBits())
    {
        case STOP_1:
            return QString("1");
#if defined(Q_OS_WIN) || defined(qdoc)
        case STOP_1_5:
            return QString("1.5");
#endif
        case STOP_2:
            return QString("2");
        }
        return 0;
}


void SerialPort::setFlowControl(const FlowType flowControl)
{
    extSerialPort.setFlowControl(flowControl);
}

QString SerialPort::getFlowControl()
{
    switch (extSerialPort.flowControl())
    {
        case FLOW_OFF:
            return QString(tr("None"));
        case FLOW_HARDWARE 	:
            return QString(tr("Hardware"));
        case FLOW_XONXOFF :
            return QString(tr("Xon/Xoff"));
        }
        return 0;
}


void SerialPort::setTimeout(ulong milliseconds)
{
    extSerialPort.setTimeout(milliseconds);
}

/*
QString SerialPort::getLastErrorToString()
{
    ulong res = extSerialPort.lastError();
    switch (res)
    {
        case E_NO_ERROR:
            return QString(tr("No Error has occured"));
        case E_INVALID_FD:
            return QString(tr("Invalid file descriptor (port was not opened correctly)"));
        case E_NO_MEMORY:
            return QString(tr("Unable to allocate memory tables (POSIX)"));
        case E_CAUGHT_NON_BLOCKED_SIGNAL:
            return QString(tr("Caught a non-blocked signal (POSIX)"));
        case E_PORT_TIMEOUT:
            return QString(tr("Operation timed out (POSIX)"));
        case E_INVALID_DEVICE:
            return QString(tr("The file opened by the port is not a character device (POSIX)"));
        case E_BREAK_CONDITION:
            return QString(tr("The port detected a break condition"));
        case E_FRAMING_ERROR:
            return QString(tr("The port detected a framing error (usually caused by incorrect baud rate settings)"));
        case E_IO_ERROR:
            return QString(tr("There was an I/O error while communicating with the port"));
        case E_BUFFER_OVERRUN:
            return QString(tr("Character buffer overrun"));
        case E_RECEIVE_OVERFLOW:
            return QString(tr("Receive buffer overflow"));
        case E_RECEIVE_PARITY_ERROR:
            return QString(tr("The port detected a parity error in the received data"));
        case E_TRANSMIT_OVERFLOW:
            return QString(tr("Transmit buffer overflow"));
        case E_READ_FAILED:
            return QString(tr("General read operation failure"));
        case E_WRITE_FAILED:
            return QString(tr("General write operation failure"));
        }
        return 0;
}*/

/*
ulong SerialPort::getLastError()
{
    return extSerialPort.lastError();
}
*/


void SerialPort::enableSending()
{
    if (!sendingEnabled && threadSend == NULL) //Si l'envoi n'est pas active && si threadSend n'est pas alloue
    {
        threadSend = new ThreadSend(&extSerialPort);
        sendingEnabled = true;
    }
}

void SerialPort::disableSending()
{
    if (sendingEnabled && threadSend != NULL) //Si l'envoi est active && si threadSend est alloue
    {
        threadSend->stopSending();
//        delete (threadSend);
        threadSend = NULL;
        sendingEnabled = false;
    }
}

bool SerialPort::isSendingEnabled()
{
    return sendingEnabled;
}

uchar SerialPort::sendData(const QByteArray &dataToSend)
{
    if (!isOpen()) //Si le port n'est pas ouvert
        return 2;

    if (!sendingEnabled || threadSend == NULL) //Si l'envoi n'est pas active || si threadSend n'est pas alloue
        return 3;

    threadSend->addDataToSend(dataToSend); //Ajout des donnees a envoyer

    return 1;
}

void SerialPort::stopSending()
{
    if (!sendingEnabled || threadSend == NULL) //Si l'envoi n'est pas active || si threadSend n'est pas été alloue
        return;

    if (threadSend->isRunning()) //si un envoi est en cour
    {
        threadSend->stopSending(); //on donne l'ordre d'arreter l'envoi
        threadSend->wait(); //on attend l'arret
    }
}



void SerialPort::enableReceiving()
{
    if (!receivingEnabled && threadReceive == NULL) //Si la reception n'est pas active && si threadReceive n'est pas alloue
    {
        threadReceive = new ThreadReceive(&extSerialPort);
        connect(threadReceive, SIGNAL(newDataReceived(const QByteArray &)), this, SIGNAL(newDataReceived(const QByteArray &)));
        receivingEnabled = true;
    }
}

void SerialPort::disableReceiving()
{
    if (receivingEnabled && threadReceive != NULL) //Si la reception est pas active && si threadReceive est alloue
    {
        threadReceive->stopReceiving();
//        delete (threadReceive);
        threadReceive = NULL;
        receivingEnabled = false;
    }
}

bool SerialPort::isReceivingEnabled()
{
    return receivingEnabled;
}

uchar SerialPort::receiveData()
{
    if (!isOpen()) //Si le port n'est pas ouvert
        return 2;
    if (!receivingEnabled || threadReceive == NULL) //Si la reception n'est pas active || si threadReceive n'est pas été alloue
        return 3;

    if (!threadReceive->isRunning())
    {
        saveStateReceiveData = true;
        threadReceive->start(); //Demarrage du thread de reception
    }
    return 1;
}

void SerialPort::stopReceiving()
{
    if (!receivingEnabled || threadReceive == NULL) //Si la reception n'est pas active || si threadReceive n'est pas alloue
        return;

    if (threadReceive->isRunning()) //Si le thread de reception est en fonctionnement
    {
        saveStateReceiveData = false;
        threadReceive->stopReceiving(); //on donne l'ordre d'arreter la reception
        threadReceive->wait(); //on attend l'arret
    }
}

/********************************************************************************************************
        * Classe ThreadSend
        *****************************************************************************************************/

ThreadSend::ThreadSend(QextSerialPort *addressSerialPort) : extSerialPort(addressSerialPort)
{
    dataToSend.clear();
    stopped=false;
}

ThreadSend::~ThreadSend()
{
    if (isRunning())
    {
        stopSending();
        wait();
    }
}


void ThreadSend::addDataToSend(const QByteArray &dataToAdd)
{
    mutexSend.lock();
    dataToSend.enqueue(dataToAdd);
    mutexSend.unlock();

    if (!isRunning())
        start();
}

void ThreadSend::stopSending()
{
    stopped=true;
}

void ThreadSend::run()
{
    QByteArray byteArray;

    qint64 lastSendMsgTime = QDateTime::currentMSecsSinceEpoch();

    forever
    {
        if (dataToSend.isEmpty() || stopped)
        {
            stopped = false;
            break;
        }

        mutexSend.lock();
        byteArray = dataToSend.dequeue();
        mutexSend.unlock();

        if ((QDateTime::currentMSecsSinceEpoch() - lastSendMsgTime) < 100)
        {
            #if defined(WIN32)
                Sleep(100);
            #else
                usleep(100000);
            #endif
        }

        lastSendMsgTime = QDateTime::currentMSecsSinceEpoch();

        int size = extSerialPort->write(byteArray);

//        qDebug()<<__FUNCTION__<<size<<byteArray.size()<<byteArray;
    }
}

/********************************************************************************************************
        * Classe ThreadReceive - A TERMINER
        *****************************************************************************************************/

ThreadReceive::ThreadReceive(QextSerialPort *addressSerialPort) : extSerialPort(addressSerialPort)
{
    stopped=false;
}

ThreadReceive::~ThreadReceive()
{
    if (isRunning())
    {
        stopReceiving();
        wait();
    }
}

void ThreadReceive::stopReceiving()
{
    stopped = true;

    emit sig_stop();
}

void ThreadReceive::run()
{
#if 1
    QEventLoop event;
    QEventLoop *eventLoop = &event;

    connect(extSerialPort, &QextSerialPort::readyRead, this, [=]
    {
        emit newDataReceived(extSerialPort->readAll());
    });
    connect(this, &ThreadReceive::sig_stop, this, [=]
    {
qDebug()<<__FILE__<<__LINE__<<"ThreadReceive 999";
        eventLoop->quit();
    });

    event.exec();
qDebug()<<__FILE__<<__LINE__<<"ThreadReceive 999";
#else
    forever
    {
        if (stopped)、
        {
            stopped = false;
            break;
        }
        int numBytes = extSerialPort->bytesAvailable();
        if (numBytes > 0)
        {
//            mutexReceive.lock();

//          char data[1024] = {0};
//			extSerialPort.read(data, numBytes);
//			data[numBytes]='\0';
//			dataReceived = data;

            const QByteArray &data = extSerialPort->readAll();

//            mutexReceive.unlock();

//            QByteArray buffer;
//            buffer.append(data);

            emit newDataReceived(data);
        }
    }
#endif

}

